home *** CD-ROM | disk | FTP | other *** search
Text File | 2007-10-18 | 42.7 KB | 1,406 lines |
- // BEGIN FLOCK GPL
- //
- // Copyright Flock Inc. 2005-2007
- // http://flock.com
- //
- // This file may be used under the terms of of the
- // GNU General Public License Version 2 or later (the "GPL"),
- // http://www.gnu.org/licenses/gpl.html
- //
- // Software distributed under the License is distributed on an "AS IS" basis,
- // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- // for the specific language governing rights and limitations under the
- // License.
- //
- // END FLOCK GPL
-
- // Constants
- const CC = Components.classes;
- const CI = Components.interfaces;
- const CR = Components.results;
- const CU = Components.utils;
-
- CU.import("resource:///modules/FlockCryptoHash.jsm");
-
- const FACEBOOK_API_CID = Components.ID("{53b077f0-6155-11db-b0de-0800200c9a66}");
- const FACEBOOK_API_CONTRACTID = "@flock.com/api/facebook;1";
-
- // From developer.facebook.com...
- const FACEBOOK_API_KEY = "7fa235c847b11f157e12586c86660f47";
- const FACEBOOK_API_SECRET = "645e5d3f9fde64b65a1a12744486b093";
- const FACEBOOK_API_VERSION = "1.0";
- const FACEBOOK_API_HOSTNAME = "http://api.facebook.com/";
- const FACEBOOK_API_ENDPOINT_URL = FACEBOOK_API_HOSTNAME + "restserver.php";
- const FACEBOOK_API_TOS_URL = "http://www.facebook.com/tos.php?api_key="
- + FACEBOOK_API_KEY
- + "&v=1.0";
- const FACEBOOK_API_SETSTATUS_URL = "http://www.facebook.com/updatestatus.php";
-
- // From nsIXMLHttpRequest.idl
- // 0: UNINITIALIZED open() has not been called yet.
- // 1: LOADING send() has not been called yet.
- // 2: LOADED send() has been called, headers and status are available.
- // 3: INTERACTIVE Downloading, responseText holds the partial data.
- // 4: COMPLETED Finished with all operations.
- const XMLHTTPREQUEST_READYSTATE_UNINITIALIZED = 0;
- const XMLHTTPREQUEST_READYSTATE_LOADING = 1;
- const XMLHTTPREQUEST_READYSTATE_LOADED = 2;
- const XMLHTTPREQUEST_READYSTATE_INTERACTIVE = 3;
- const XMLHTTPREQUEST_READYSTATE_COMPLETED = 4;
-
- const HTTP_CODE_OK = 200;
- const HTTP_CODE_FOUND = 302;
-
- const XMLHTTPREQUEST_CONTRACTID = "@mozilla.org/xmlextras/xmlhttprequest;1";
- const FLOCK_ERROR_CONTRACTID = "@flock.com/error;1";
-
- const FLOCK_PHOTO_ALBUM_CONTRACTID = "@flock.com/photo-album;1";
-
- const FLOCK_SNOWMAN_URL = "chrome://browser/skin/flock/services/common/no_avatar.png";
-
- function _getLoader() {
- return CC["@mozilla.org/moz/jssubscript-loader;1"]
- .getService(CI.mozIJSSubScriptLoader);
- }
-
- function loadLibraryFromSpec(aSpec) {
- _getLoader().loadSubScript(aSpec);
- }
-
- loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
-
- function createEnum(array) {
- return {
- QueryInterface: function (iid) {
- if (iid.equals(CI.nsISimpleEnumerator)) {
- return this;
- }
- throw CR.NS_ERROR_NO_INTERFACE;
- },
- hasMoreElements: function() {
- return (array.length>0);
- },
- getNext: function() {
- return array.shift();
- }
- }
- }
-
-
- function MyBag() {
- }
-
- MyBag.prototype.setProperty =
- function mybag_setProperty(aKey, aValue) {
- this[aKey] = aValue;
- };
-
- MyBag.prototype.setPropertyAsInt32 =
- function mybag_setPropertyAsInt32(aKey, aValue) {
- this.setProperty(aKey, aValue);
- };
-
- MyBag.prototype.setPropertyAsAString =
- function mybag_setPropertyAsString(aKey, aValue) {
- this.setProperty(aKey, aValue);
- };
-
- MyBag.prototype.getProperty =
- function mybag_getProperty(aKey) {
- return this[aKey];
- };
-
- MyBag.prototype.getPropertyAsInt32 =
- function mybag_getPropertyAsString(aKey) {
- return this.getProperty(aKey);
- };
-
- MyBag.prototype.getPropertyAsAString =
- function mybag_getPropertyAsString(aKey) {
- return this.getProperty(aKey);
- };
-
- MyBag.prototype.QueryInterface =
- function mybag_QueryInterface(aIid) {
- return this;
- };
-
- function _validateUid(aUid) {
- if (aUid.match(/[^0-9]/)) {
- throw "Invalid UID '" + aUid + "': the UID must be a numeric value";
- }
- }
-
- // ====================================================
- // ========== BEGIN flockIPhoto object ==========
- // ====================================================
-
- function facebookPhoto() {
- }
-
- facebookPhoto.prototype = {
- id: "",
- thumbnail: "",
- webPageUrl: "",
- midSizePhoto: "",
- largeSizePhoto: "",
- title: "",
- username: "",
- userid: "",
- is_public: true,
- is_video: false,
- svcShortName: "facebook",
- buildTooltip: function facebookPhoto_buildTooltip() {
- default xml namespace =
- "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
- var tip =
- <vbox>
- <hbox>
- <image src={this.midSizePhoto} style="margin-bottom: 2px;" />
- <spacer flex="1" />
- </hbox>
- <hbox>
- <image src={this.icon} />
- <vbox style="max-width: 250px;">
- <label crop="end">{this.title}</label>
- <label class="user">{this.username}</label>
- </vbox>
- </hbox>
- </vbox>;
-
- var parser = CC["@mozilla.org/xmlextras/domparser;1"]
- .createInstance(CI.nsIDOMParser);
- var doc = parser.parseFromString(tip, "text/xml");
- return doc.documentElement;
- },
-
- buildHTML: function facebookPhoto_buildHTML() {
- var xml =
- <a title={this.title} href={this.webPageUrl}>
- <img src={this.midSizePhoto} border="0" />
- </a>
- return xml;
- },
-
- buildLargeHTML: function facebookPhoto_buildLargeHTML() {
- var xml =
- <a title={this.title} href={this.webPageUrl}>
- <img src={this.largeSizePhoto} border="0" />
- </a>
- return xml;
- },
-
- buildBBCode: function facebookPhoto_buildBBCode() {
- return '[url=' + this.webPageUrl + '][img]'+ this.largeSizePhoto +'[/img][/url]';
- },
-
- QueryInterface: function facebookPhoto_QueryInterface(aIid) {
- if (!aIid.equals(CI.nsISupports) &&
- !aIid.equals(CI.flockIPhoto)) {
- throw CR.NS_ERROR_NO_INTERFACE;
- }
- return this;
- }
- };
-
- /**
- * Represents the Facebook API
- */
- function facebookAPI() {
- this.api_key = FACEBOOK_API_KEY;
- this.api_secret = FACEBOOK_API_SECRET;
- this.endpoint = FACEBOOK_API_ENDPOINT_URL;
- this.prettyName = "Facebook";
- this.uid = null;
- this._friendsCache = {
- value: null,
- lastUpdate: new Date(0)
- };
-
- this.acUtils = CC["@flock.com/account-utils;1"]
- .getService(CI.flockIAccountUtils);
- var obService = CC["@mozilla.org/observer-service;1"]
- .getService(CI.nsIObserverService);
- var inst = this;
- this.webDetective = CC["@flock.com/web-detective;1"]
- .getService(CI.flockIWebDetective);
-
- this._logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
- this._logger.init("facebookAPI");
- this.wrappedJSObject = this;
- }
-
- //****************************************
- //* Authentication & Sesssion Calls
- //****************************************/
-
- facebookAPI.prototype.sessionPing =
- function facebookAPI_sessionPing(aListener) {
- var pingListener = {
- onSuccess: function onSuccess(aResult) {
- aListener.onSuccess(null, aResult.result);
- },
- onError: function onError(aError) {
- aListener.onError(null, "", null);
- }
- }
- this.authCallJSON(pingListener, "facebook.session.ping");
- }
-
- facebookAPI.prototype.authenticate =
- function facebookAPI_authenticate(aListener) {
- this._logger.debug("authenticate()");
- var api = this;
- var tokenListener = {
- onSuccess: function(aToken) {
- var authUrl = api.getAuthUrl(aToken);
-
- var req = CC[XMLHTTPREQUEST_CONTRACTID].
- createInstance(CI.nsIXMLHttpRequest);
-
- // We try to authenticate in the normal fashion (apiAuth2) first.
- // If that fails, it may be because we have yet to agree to the
- // Facebook Platform User Terms of Service. apiAuth1 handles that case.
- var onReadyStateFunc = function onReadyStateFunc(eEvt) {
- if (req.readyState == XMLHTTPREQUEST_READYSTATE_COMPLETED) {
- if (api.webDetective.detectNoDOM("facebook", "apiAuth2", "",
- req.responseText, null))
- {
- api.getSession(aToken, aListener);
- } else {
- api._logger.debug("req: \n" + req.responseText);
- var results = new MyBag();
- if (api.webDetective.detectNoDOM("facebook",
- "apiAuth1",
- "",
- req.responseText,
- results))
- {
- var postBody = "grant_perm=1"
- + "&next="
- + "&api_key="
- + FACEBOOK_API_KEY
- + "&auth_token="
- + aToken
- + "&post_form_id="
- + results.getPropertyAsAString("formId");
-
- var req2 = CC[XMLHTTPREQUEST_CONTRACTID].
- createInstance(CI.nsIXMLHttpRequest);
-
- req2.onreadystatechange = function (eEvt) {
- if (req2.readyState == XMLHTTPREQUEST_READYSTATE_COMPLETED) {
- if (api.webDetective.detectNoDOM("facebook",
- "apiAuth2",
- "",
- req2.responseText,
- null))
- {
- api.getSession(aToken, aListener);
- } else {
- api._logger.debug(req.responseText);
- var error = CC[FLOCK_ERROR_CONTRACTID]
- .createInstance(CI.flockIError);
- error.errorString = "apiAuth2 Failed.";
- api._logger.debug("apiAuth2 Failed.");
- aListener.onError(null, null, error);
- }
- }
- }
-
- req2.detachLoadGroup = true;
- req2.open("POST", results.getPropertyAsAString("postUrl"), true);
- req2.setRequestHeader("Content-Type",
- "application/x-www-form-urlencoded");
- req2.overrideMimeType("text/txt");
- req2.send(postBody);
- } else {
- var error = CC[FLOCK_ERROR_CONTRACTID]
- .createInstance(CI.flockIError);
- error.errorString = "apiAuth1 Failed.";
- api._logger.debug("apiAuth1 Failed.");
- aListener.onError(null, null, error);
- }
- }
- }
- }
-
- req.onreadystatechange = onReadyStateFunc;
- req.detachLoadGroup = true;
- req.open("GET", authUrl, true);
- req.overrideMimeType("text/txt");
- req.send(null);
- },
- onError: function onError(aErrorCode) {
- aListener.onError(null, "", aErrorCode);
- }
- };
- this.createToken(tokenListener);
- }
-
- facebookAPI.prototype.deauthenticate =
- function facebookAPI_deauthenticate() {
- this._logger.debug(".deauthenticate()");
- this.session_key = null;
- this.secret = null;
- this.uid = null;
- this._friendsCache.value = null;
- this.isLoggedIn = false;
- }
-
-
- // INTERNAL
- facebookAPI.prototype.createToken =
- function facebookAPI_createToken(aListener) {
- this._logger.debug("createToken()\n");
- var api = this;
- var tokenListener = {
- onSuccess: function onSuccess(aXml) {
- api._logger.debug(".createToken().onSuccess " + aXml);
- try {
- var token = aXml.getElementsByTagName("token")[0].firstChild.nodeValue;
- aListener.onSuccess(token);
- } catch (err) {
- try {
- var token = aXml.getElementsByTagName("auth_createToken_response")[0]
- .firstChild
- .nodeValue;
- aListener.onSuccess(token);
- } catch (err) {
- var error = CC[FLOCK_ERROR_CONTRACTID]
- .createInstance(CI.flockIError);
- error.errorString = err;
- aListener.onError(null, "", error);
- }
- }
- },
- onError: function onError(aErrorCode) {
- aListener.onError(null, "", aErrorCode);
- }
- };
- this.call(tokenListener, "facebook.auth.createToken");
- }
-
- // INTERNAL
- facebookAPI.prototype.getSession =
- function facebookAPI_getSession(aToken, aListener) {
- var api = this;
- var aParams = {
- auth_token: aToken
- };
- var sessionListener = {
- onSuccess: function onSuccess(aXml) {
- api.session_key = aXml.getElementsByTagName("session_key")[0]
- .firstChild
- .nodeValue;
- api.secret = aXml.getElementsByTagName("secret")[0]
- .firstChild
- .nodeValue;
- api.uid = aXml.getElementsByTagName("uid")[0]
- .firstChild
- .nodeValue;
- api._friendsCache.value = null;
- api.isLoggedIn = true;
- aListener.onSuccess(null, "authenticated")
- },
- onError: function onError(aErrorCode) {
- api.isLoggedIn = false;
- aListener.onError(null, "", aErrorCode);
- }
- };
- api.isLoggedIn = false;
- api.call(sessionListener, "facebook.auth.getSession", aParams);
- }
-
- // INTERNAL
- facebookAPI.prototype.getAuthUrl =
- function facebookAPI_getAuthUrl(aToken) {
- return "http://www.facebook.com/login.php?api_key=" + this.api_key
- + "&v=" + FACEBOOK_API_VERSION + "&auth_token=" + aToken;
- }
-
- facebookAPI.prototype.getFacebookURL =
- function facebookAPI_getFacebookURL(aUrlType, aFriendId) {
-
- switch (aUrlType) {
- case "profile":
- return "http://www.facebook.com/profile.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "myprofile":
- return "http://www.facebook.com/profile.php?id="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "poke":
- return "http://www.facebook.com/poke.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "message":
- return "http://www.facebook.com/message.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "editprofile":
- return "http://www.facebook.com/editprofile.php";
- break;
- case "photos":
- return "http://www.facebook.com/photo_search.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "userphotos":
- return "http://www.facebook.com/photo_search.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "myphotos":
- return "http://www.facebook.com/photos.php?id="
- + aFriendId;
- break;
- case "postwall":
- return "http://www.facebook.com/wallpost.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "addfriend":
- return "http://www.facebook.com/addfriend.php?uid="
- + aFriendId
- + "&api_key="
- + this.api_key;
- break;
- case "friendrequests":
- return "http://www.facebook.com/reqs.php";
- break;
- case "messages":
- return "http://www.facebook.com/mailbox.php";
- break;
- case "homepage":
- return "http://www.facebook.com/home.php";
- break;
- }
- return "";
- }
-
- //****************************************
- //* Friends Calls
- //****************************************/
-
-
- facebookAPI.prototype.friendsGet =
- function facebookAPI_friendsGet(aListener) {
- var api = this;
- var listener = {
- onSuccess: function listener_onSuccess(aResult) {
- var peeps = {};
- for (var i in aResult) {
- var uid = aResult[i].uid;
- peeps[uid] = aResult[i];
-
- if (!peeps[uid].pic_square) {
- peeps[uid].pic_square = "";
- }
-
- if (!peeps[uid].status) {
- peeps[uid].status = {
- time: 0,
- message: ""
- }
- }
-
- api._logger.debug("Got facebook person named "
- + peeps[uid].name);
- }
-
- var result = {
- wrappedJS: peeps
- };
- aListener.onSuccess(result, "success");
- },
- onError: function(errorCode) {
- api._logger.debug(".friendsGet() error: " + errorCode);
- }
- }
-
- // For security let's make sure this.uid is what
- // it's supposed to be: just a number
- _validateUid(this.uid);
-
- var friendsQuery = "SELECT uid,name,pic_square,status,profile_update_time "
- + "FROM user "
- + "WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = "+this.uid+")";
-
- this.authCallJSON(listener, "facebook.fql.query", { query: friendsQuery });
- }
-
-
- facebookAPI.prototype.getUpdatedMediaFriends =
- function facebookAPI_getUpdatedMediaFriends(aListener, aLastSeen) {
- var api = this;
- var listener = {
- onSuccess: function gumfListener_onSuccess(aResult) {
- // We put everything in a hash first to avoid duplicates
- var friends = {};
- for (var i in aResult) {
- var owner = aResult[i]["owner"];
- // 10 for Base 10
- var created = parseInt(aResult[i]["created"], 10);
- if (friends[owner]) {
- friends[owner].count++;
- if (created > friends[owner].latest) {
- friends[owner].latest = created;
- }
- } else {
- friends[owner] = {
- count: 1,
- latest: created
- };
- }
- }
- var result = [];
- for (i in friends) {
- var person = new MyBag();
- person.setPropertyAsAString("uid", i);
- person.setPropertyAsInt32("count", friends[i].count);
- person.setPropertyAsInt32("latest", friends[i].latest);
- result.push(person);
- }
- aListener.onSuccess(createEnum(result), "success");
- },
- onError: function gumfListener_onError(errorCode) {
- api._logger.debug(".getUpdatedMediaFriends() error: " + errorCode);
- }
- }
-
- // For security let's make sure this.uid is what
- // it's supposed to be: just a number
- _validateUid(this.uid);
-
- var friendsQuery = "SELECT uid1 "
- + "FROM friend "
- + "WHERE uid2=" + this.uid;
- var albumQuery = "SELECT aid "
- + "FROM album "
- + "WHERE owner IN " + "(" + friendsQuery + ")";
- var query = "SELECT owner,created "
- + "FROM photo "
- + "WHERE created > " + aLastSeen
- + " AND " + "aid IN (" + albumQuery + ")";
-
- this.authCallJSON(listener, "facebook.fql.query", { query: query });
- };
-
- //****************************************
- //* Users Info Call
- //****************************************/
-
- facebookAPI.prototype.usersGetInfo =
- function facebookAPI_usersGetInfo(aListener, users, fields) {
- var params = {
- uids: users,
- fields: "name,pic_square,status"
- }
-
- if (fields) {
- this._logger.debug("WARNING - arbitrary fields are not handled yet");
- params.fields = fields;
- }
-
- var api = this;
- var listener = {
- onSuccess: function ugiListener_onSuccess(aResult) {
- var peeps = [];
- for (var i in aResult) {
- var user = aResult[i];
- var person = new MyBag();
- person.setPropertyAsAString("uid", user.uid);
- person.setPropertyAsAString("name", user.name);
- person.setPropertyAsAString("avatar", user.pic_square);
- if (user.status) {
- person.setPropertyAsAString("statusMessage",
- user.status.message);
- }
-
- peeps.push(person);
- }
-
- aListener.onSuccess(createEnum(peeps), "success");
- },
- onError: function ugiListener_onError(aError) {
- api._logger.debug(".usersGetInfo() error:" + aError.errorString);
- }
- }
-
- this.authCallJSON(listener, "facebook.users.getInfo", params);
- }
-
-
- facebookAPI.prototype.notificationsGet =
- function facebookAPI_notificationsGet(aListener) {
- // FIXME: Use JSON or at least E4X (bug 9573)
- var api = this;
- var listener = {
- onSuccess: function ngListener_onSuccess(aXml) {
- var notifications = [];
-
- var meNotifications = new MyBag();
- meNotifications.setPropertyAsAString("messages",
- aXml.getElementsByTagName("messages")[0]
- .getElementsByTagName("unread")[0]
- .childNodes[0]
- .nodeValue);
- meNotifications.setPropertyAsAString("pokes",
- aXml.getElementsByTagName("pokes")[0]
- .getElementsByTagName("unread")[0]
- .firstChild
- .nodeValue);
- meNotifications.setPropertyAsAString("friendRequests",
- aXml.getElementsByTagName("friend_requests")[0]
- .getElementsByTagName("uid")
- .length);
- meNotifications.setPropertyAsAString("groupInvites",
- aXml.getElementsByTagName("group_invites")[0]
- .getElementsByTagName("gid")
- .length);
- meNotifications.setPropertyAsAString("eventInvites",
- aXml.getElementsByTagName("event_invites")[0]
- .getElementsByTagName("eid")
- .length);
- notifications.push(meNotifications);
-
- aListener.onSuccess(createEnum(notifications), "success");
- },
- onError: function ngListener_onError(aError) {
- api._logger.debug(".notificationsGet() error:" + aError.errorString);
- }
- };
-
- this.authCall(listener, "facebook.notifications.get");
- }
-
-
- facebookAPI.prototype.setStatus =
- function facebookAPI_setStatus(aNewStatus, aListener) {
- var api = this;
-
- function reallySetStatus(aPostFormId) {
- var params = {
- post_form_id: aPostFormId
- };
-
- if (aNewStatus && aNewStatus != "") {
- params.status = aNewStatus;
- } else {
- params.clear = "1";
- }
-
- var body = api.getParamString(params);
-
- var xhr2 = CC["@mozilla.org/xmlextras/xmlhttprequest;1"]
- .createInstance(CI.nsIXMLHttpRequest);
- xhr2.open ("POST", FACEBOOK_API_SETSTATUS_URL, true);
- xhr2.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
- xhr2.setRequestHeader("Content-Length", body.length);
-
- xhr2.onreadystatechange = function (aEvent) {
- if (xhr2.readyState == XMLHTTPREQUEST_READYSTATE_COMPLETED) {
- if ((xhr2.status != HTTP_CODE_OK) &&
- (xhr2.status != HTTP_CODE_FOUND))
- {
- if (aListener) {
- aListener.onError(xhr2.statusText);
- }
- return;
- }
- if (aListener) {
- aListener.onSuccess(null, "");
- }
- }
- }
-
- xhr2.send(body);
- }
-
- function viewTOS() {
- // Show the TOS and to get the form
- var xhr = CC["@mozilla.org/xmlextras/xmlhttprequest;1"]
- .createInstance(CI.nsIXMLHttpRequest);
- xhr.open("GET", FACEBOOK_API_TOS_URL, true);
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
-
- xhr.onreadystatechange = function (aEvent) {
- if (xhr.readyState == XMLHTTPREQUEST_READYSTATE_COMPLETED) {
- if (xhr.status != HTTP_CODE_OK) {
- if (aListener) {
- aListener.onError (xhr.statusText);
- }
- return;
- }
-
- // Get the post_form_id and setStatus for real
- var postFormId;
- var results = CC["@mozilla.org/hash-property-bag;1"]
- .createInstance(CI.nsIWritablePropertyBag2);
- try {
- if (api.webDetective.detectNoDOM("facebook",
- "setStatus",
- "",
- xhr.responseText,
- results))
- {
- postFormId = results.getPropertyAsAString("status");
- reallySetStatus(postFormId);
- }
- } catch (ex) {
- aListener.onError();
- }
- }
- }
- xhr.send(null);
- }
-
- viewTOS();
- }
-
-
- //****************************************
- //* Photo Call
- //****************************************/
-
- facebookAPI.prototype.getAlbums =
- function facebookAPI_getAlbums(aListener, userID) {
- var api = this;
- var listener = {
- onSuccess: function(xml) {
- var albums = [];
- var eAlbums = xml.getElementsByTagName('album')
- for (var i=0; i<eAlbums.length; i++) {
- var id = eAlbums[i].getElementsByTagName('aid')[0]
- .firstChild
- .nodeValue;
- var title = eAlbums[i].getElementsByTagName('name')[0]
- .firstChild
- .nodeValue;
-
- var newAlbum = CC[FLOCK_PHOTO_ALBUM_CONTRACTID]
- .createInstance(CI.flockIPhotoAlbum);
- newAlbum.id = id;
- newAlbum.title = title;
- api._logger.debug(".getAlbums() Found album: title = " + title +
- ", id = " + id);
- albums.push(newAlbum);
- }
- aListener.onSuccess(createEnum(albums), 'success');
- },
- onError: function(aFlockError) {
- api._logger.debug(".getAlbums() error: " + aFlockError.errorString);
- aListener.onError(null, null, aFlockError);
- }
- }
- var params = {
- uid: userID
- }
- this.authCall(listener, 'facebook.photos.getAlbums', params);
- }
-
-
- facebookAPI.prototype.createAlbum =
- function facebookAPI_createAlbum(aListener, aAlbumTitle) {
- var api = this;
- var listener = {
- onSuccess: function ca_onSuccess(aXml) {
- var id = aXml.getElementsByTagName("aid")[0].firstChild.nodeValue;
- var title = aXml.getElementsByTagName("name")[0].firstChild.nodeValue;
-
- var newAlbum = CC[FLOCK_PHOTO_ALBUM_CONTRACTID]
- .createInstance(CI.flockIPhotoAlbum);
- newAlbum.title = title;
- newAlbum.id = id;
- aListener.onSuccess(newAlbum, "success");
- },
- onError: function ca_onError(aFlockError) {
- api._logger.debug(".createAlbum() error: " + aFlockError.errorString);
- aListener.onError(aFlockError);
- }
- }
- var params = {
- name: aAlbumTitle
- }
- this.authCall(listener, "facebook.photos.createAlbum", params);
- }
-
-
- facebookAPI.prototype.getPhotos =
- function facebookAPI_getPhotos(aListener, aSubjectId, aAlbumId, aPidList) {
- var api = this;
- var listener = {
- onSuccess: function gp_onSuccess(aResult) {
- var photos = [];
- for (var i in aResult) {
- var photo = aResult[i];
-
- var newPhoto = new facebookPhoto();
- newPhoto.id = photo.pid;
- newPhoto.webPageUrl = photo.link;
- newPhoto.thumbnail = photo.src_small;
- newPhoto.midSizePhoto = photo.src;
- newPhoto.largeSizePhoto = photo.src_big;
- newPhoto.userid = photo.owner;
- newPhoto.title = photo.caption ? photo.caption : "";
- newPhoto.uploadDate = parseInt(photo.created) * 1000;
- // FIXME: need to get the full name and avatar of user - DP
- newPhoto.username = photo.owner;
- newPhoto.icon = FLOCK_SNOWMAN_URL;
-
- photos.push(newPhoto);
- }
- aListener.onSuccess(createEnum(photos), 'success');
- },
- onError: function gp_onError(flockError) {
- api._logger.debug(".getPhotos() error: " + flockError.errorString);
- aListener.onError(null, null, flockError);
- }
- };
- var params = {};
- if (aSubjectId) {
- params.subj_id = aSubjectId;
- }
- if (aAlbumId) {
- params.aid = aAlbumId;
- }
- if (aPidList) {
- params.pids = aPidList;
- }
- this.authCallJSON(listener, "facebook.photos.get", params);
- }
-
-
- facebookAPI.prototype._finalizePhoto =
- function facebookAPI__finalizePhoto(aUploadListener,
- aUpload,
- aId)
- {
- var getPhotoListener = {
- onSuccess: function fp_onSuccess(aPhotos) {
- if (aPhotos.hasMoreElements()) {
- var photo = aPhotos.getNext();
- photo.QueryInterface(CI.flockIPhoto);
- aUploadListener.onUploadFinalized(aUpload, photo);
- } else {
- // Empty result
- aUploadListener.onError(null);
- }
- },
- onError: function fp_onError(aError) {
- aUploadListener.onError(null);
- }
- };
-
- this.getPhotos(getPhotoListener, null, null, aId);
- }
-
- facebookAPI.prototype.uploadPhotosViaUploader =
- function facebookAPI_uploadPhotosViaUploader(aListener,
- aPhoto,
- aParams,
- aUpload) {
- var api = this;
-
- var pid;
- var listener = {
- onResult: function listener_onResult(aXml) {
- api._logger.debug(CC["@mozilla.org/xmlextras/xmlserializer;1"]
- .getService(CI.nsIDOMSerializer)
- .serializeToString(aXml));
- var resp = aXml.getElementsByTagName("photos_upload_response")[0];
- if (!resp) {
- var error = api.getXMLError(aXml);
- aListener.onError(error);
- } else {
- pid = aXml.getElementsByTagName("pid")[0].childNodes[0].nodeValue;
-
- // tag photo if user requested
- if (aUpload.notes) {
- var addTagListener = {
- onSuccess: function addTagListener_success(aXml, aStatus) {
- api._logger.debug("tagged successfully\n");
- },
- onError: function addTagListener_error(aSubject, aTopic, aError) {
- api._logger.error("error tagging photo\n");
- }
- };
- api.addTag(addTagListener, pid, "", "", "", "", aUpload.notes);
- }
-
- aListener.onUploadComplete(aUpload);
- api._finalizePhoto(aListener, aUpload, pid);
- }
- },
- onError: function listener_onError(aErrorCode) {
- if (aErrorCode) {
- aListener.onError(api.getHTTPError(aErrorCode));
- } else {
- aListener.onError(api.getError(null, null));
- }
- },
- onProgress: function listener_onProgress(aCurrentProgress) {
- aListener.onProgress(aCurrentProgress);
- }
- };
-
- var params = {};
- params.method = "facebook.photos.upload";
- params.api_key = this.api_key;
- params.session_key = this.session_key;
- params.call_id = new Date().getTime();
- params.v = FACEBOOK_API_VERSION;
- params.aid = aUpload.album;
- params.caption = aParams.getProperty("description");
-
- var uploader = new PhotoUploader();
-
- params = api.appendSignature(params, api.secret);
- uploader.setEndpoint(FACEBOOK_API_ENDPOINT_URL);
- uploader.upload(listener, aPhoto, params);
-
- return;
- }
-
- facebookAPI.prototype.addTag =
- function facebookAPI_addTag(aListener, aPid, aTagUid, aTagText, aX, aY, aTags) {
- this._logger.debug(".addTag()");
- var json;
- if (aTags) {
- json = aTags;
- } else {
- json = '[{"x":"' + aX + '","y":"' + aY + '"';
- if (aTagUid) {
- json += ',"tag_uid":' + aTagUid;
- } else if (aTagText) {
- json += ',"tag_text":"' + aTagText + '"';
- }
- json += '}]';
- }
- this._logger.debug("tag json: " + json);
-
- var myListener = {
- onSuccess : function(xml) {
- aListener.onSuccess(xml, "success");
- },
- onError: function(aFlockError) {
- aListener.onError(null, null, aFlockError);
- }
- };
-
- var params = {
- pid: aPid,
- tags: json
- };
- this.authCall(myListener, "facebook.photos.addTag", params);
- }
-
- //****************************************
- //* FQL Query calls
- //****************************************/
- facebookAPI.prototype.doFQLQuery=
- function facebookAPI_doFQLQuery(aListener, aFQLQuery) {
-
- var myListener = {
- onSuccess : function(xml) {
- aListener.onSuccess(xml, "success");
- },
- onError: function(aFlockError) {
- aListener.onError(null, null, aFlockError);
- }
- };
-
- var params = {
- format: "xml",
- query: aFQLQuery
- };
- this.authCall(myListener, 'facebook.fql.query', params);
- }
-
-
- //******************************************
- //* internal functions for making requests
- //******************************************
-
- /* --- authCall is used for authenticated (most) calls --- */
-
- facebookAPI.prototype.authCall =
- function facebookAPI_authCall(aListener, aMethod, aParams, aIsDryRun) {
- if (!aParams) aParams = {};
- aParams.api_key = this.api_key;
- aParams.session_key = this.session_key;
- aParams.call_id = new Date().getTime();
- aParams.method = aMethod;
- aParams.v = FACEBOOK_API_VERSION;
- var paramString = this.getParamString(aParams, this.secret);
- var url = this.endpoint + "?" + paramString;
- this._logger.debug("===[" + aMethod + "]===> " + url);
- if (!aIsDryRun) {
- this._doCall(aListener, url, null);
- }
- }
-
- facebookAPI.prototype.authCallJSON =
- function facebookAPI_authCallJSON(aListener, aMethod, aParams, aIsDryRun) {
- if (!aParams) aParams = {};
- aParams.api_key = this.api_key;
- aParams.session_key = this.session_key;
- aParams.call_id = new Date().getTime();
- aParams.method = aMethod;
- aParams.format = "JSON";
- aParams.v = FACEBOOK_API_VERSION;
- var paramString = this.getParamString(aParams, this.secret);
- var url = this.endpoint + "?" + paramString;
- this._logger.debug("===[" + aMethod + "]===> " + url);
- if (!aIsDryRun) {
- this._doCallJSON(aListener, url, null);
- }
- }
-
- /* --- call is only used to start authentication by getting a token --- */
-
- facebookAPI.prototype.call =
- function facebookAPI_call(aListener, aMethod, aParams, aIsDryRun) {
- if (!aParams) aParams = {};
- aParams.api_key = this.api_key;
- aParams.method = aMethod;
- aParams.v = FACEBOOK_API_VERSION;
- var paramString = this.getParamString(aParams, this.api_secret);
- var url = this.endpoint + "?" + paramString;
- this._logger.debug("===[" + aMethod + "]===> " + url);
- if (!aIsDryRun) {
- this._doCall(aListener, url, null);
- }
- }
-
- /* --- convert dictionary to a request string, adding the signature --- */
-
- facebookAPI.prototype.getParamString =
- function facebookAPI_getParamString(aParams, secret) {
- var params = this.appendSignature(aParams, secret);
- var rval = "";
-
- var count = 0;
- for(var p in params) {
- if(count++!=0) rval += "&";
- rval += encodeURIComponent(p) + "=" + encodeURIComponent(params[p]);
- }
-
- return rval;
- };
-
- /* --- calculate and add the signature to the request parameters --- */
-
- facebookAPI.prototype.appendSignature =
- function facebookAPI_appendSignature(aParams, secret) {
- var keys = new Array();
-
- for (var p in aParams) keys.push(p);
- keys.sort();
-
- var preHash = '';
- for (var i=0; i<keys.length; ++i) preHash += keys[i] + '=' + aParams[keys[i]];
- preHash += secret;
-
- var converter = CC["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(CI.nsIScriptableUnicodeConverter);
-
- converter.charset = "UTF-8";
- var inputStream = converter.convertToInputStream(preHash);
- var newParams = aParams;
- newParams.sig = FlockCryptoHash.md5Stream(inputStream);
- return newParams;
- };
-
-
- facebookAPI.prototype.getHTTPError =
- function facebookAPI_getHTTPError(aHTTPErrorCode) {
- var error = CC[FLOCK_ERROR_CONTRACTID].createInstance(CI.flockIError);
- error.errorCode = aHTTPErrorCode;
-
- return error;
- }
-
- facebookAPI.prototype.getXMLError =
- function facebookAPI_getXMLError(aXML) {
- // FIXME: Use E4X (bug 9573)
- var fbErrorCode;
- var fbErrorMessage;
-
- // <error_response xmlns="http://api.facebook.com/1.0/">
- // <error_code>324</error_code>
- // <error_msg>Missing or invalid image file</error_msg>
- // <request_args/>
- // </error_response>
- try {
- fbErrorCode = aXML.getElementsByTagName("error_code")[0]
- .childNodes[0].nodeValue;
- fbErrorMessage = aXML.getElementsByTagName("error_msg")[0]
- .childNodes[0].nodeValue;
- } catch (ex) {
- // <result method="" type="struct">
- // <fb_error type="struct">
- // <code>101</code>
- // <msg>Invalid API key</msg>
- // <your_request/>
- // </fb_error>
- // </result>
- try {
- fbErrorCode = aXML.getElementsByTagName("code")[0]
- .childNodes[0].nodeValue;
- fbErrorMessage = aXML.getElementsByTagName("msg")[0]
- .childNodes[0].nodeValue;
- } catch (ex2) {
- // in case the error xml is invalid
- fbErrorCode = "999";
- }
- }
-
- return this.getError(fbErrorCode, fbErrorMessage);
- }
-
-
- facebookAPI.prototype.getError =
- function facebookAPI_getError(aFBErrorCode, aFBErrorMessage) {
- var error = CC[FLOCK_ERROR_CONTRACTID].createInstance(CI.flockIError);
-
- this._logger.debug(".getError() Facebook error code: " + aFBErrorCode + "\n");
- switch (aFBErrorCode) {
- case "2":
- // 2: The service is not available at this time.
- error.errorCode = CI.flockIError.PHOTOSERVICE_UNAVAILABLE;
- break;
- case "100":
- // 100: One of the parameters specified was missing or invalid.
- case "103":
- // 103: The submitted call_id was not greater than the previous call_id
- // for this session.
- case "104":
- // 104: Incorrect signature.
- error.errorCode = CI.flockIError.PHOTOSERVICE_INVALID_QUERY;
- break;
- case "101":
- // 101: The api key submitted is not associated with any known
- // application.
- error.errorCode = CI.flockIError.PHOTOSERVICE_INVALID_API_KEY;
- break;
- case "102":
- // 102: The session key was improperly submitted or has reached its
- // timeout. Direct the user to log in again to obtain another key.
- error.errorCode = CI.flockIError.PHOTOSERVICE_USER_NOT_LOGGED_IN;
- error.serviceName = this.prettyName;
- break;
- case "321":
- // 321: Album is full
- error.errorCode = CI.flockIError
- .PHOTOSERVICE_PHOTOS_IN_ALBUM_LIMIT_REACHED;
- break;
- case "1":
- // 1: An unknown error occurred. Please resubmit the request.
- case "999":
- error.errorCode = CI.flockIError.PHOTOSERVICE_UNKNOWN_ERROR;
- break;
- default:
- error.errorCode = CI.flockIError.PHOTOSERVICE_UNKNOWN_ERROR;
- error.serviceErrorString = serviceErrorMessage;
- }
- return error;
- }
-
- /* --- actually make the http request --- */
-
- facebookAPI.prototype._doCall =
- function facebookAPI_doCall(aListener, aUrl, aContent) {
- var api=this;
-
- // copied from flickr service --- potential issues:
- // 1) shouldn't be using xmlhttprequest
- // 2) keeping a reference to the request via this.req
- // keeps it referenced after we are done with it
- // (until the next call)
-
- this.req = CC[XMLHTTPREQUEST_CONTRACTID].
- createInstance(CI.nsIXMLHttpRequest);
- this.req.QueryInterface(CI.nsIJSXMLHttpRequest);
- this.req.open("GET", aUrl, true);
- var req = this.req;
- this.req.onreadystatechange = function (aEvt) {
- api._logger.debug("._doCall() ReadyState = " + req.readyState);
- if (req.readyState == XMLHTTPREQUEST_READYSTATE_COMPLETED) {
- try {
- api._logger.debug("._doCall() Status = " + req.status);
- if(req.status/100 == 2) {
- try {
- api._logger.debug("._doCall() response = \n" + req.responseText);
- var fb_error = req.responseXML
- .getElementsByTagName("error_response");
- if (fb_error.length > 0) {
- api._logger.debug("._doCall() Error");
- aListener.onError(api.getXMLError(req.responseXML));
- } else {
- api._logger.debug("._doCall() Success");
- aListener.onSuccess(req.responseXML);
- }
- } catch (ex) {
- // error parsing xml
- api._logger.error("._doCall() Parse error = " + ex);
- aListener.onError(api.getError(null, null));
- }
- } else {
- // http errors
- api._logger.debug("._doCall() HTTP Error");
- aListener.onError(api.getHTTPError(req.status));
- }
- } catch (ex) {
- // XMLHTTPERROR (connection lost)
- api._logger.debug(".doCall() " + ex);
- aListener.onError(api.getHTTPError("9001"));
- }
- }
- }
- try {
- // just for debug output
- aUrl.match(/call_id=(.+)&/);
- var call_id = RegExp.$1;
- this._logger.debug("sending call_id: " + call_id);
- } catch (ex) {
- }
- this.req.send(null);
- };
-
- facebookAPI.prototype._doCallJSON =
- function facebookAPI_doCallJSON(aListener, aUrl, aContent) {
- var api = this;
-
- this.req = CC[XMLHTTPREQUEST_CONTRACTID]
- .createInstance(CI.nsIXMLHttpRequest);
- this.req.QueryInterface(CI.nsIJSXMLHttpRequest);
- this.req.open("GET", aUrl, true);
- var req = this.req;
- this.req.onreadystatechange =
- function doCallJSON_onreadystatechange(aEvt) {
- api._logger.debug("._doCall() ReadyState = " + req.readyState);
- if (req.readyState == XMLHTTPREQUEST_READYSTATE_COMPLETED) {
- try {
- api._logger.debug("._doCall() Status = " + req.status);
- if (req.status/100 == 2) {
- api._logger.debug("._doCall() response = \n" + req.responseText);
- var s = new Components.utils.Sandbox("about:blank");
- var result = Components.utils.evalInSandbox(req.responseText, s);
- if (result && result.error_code && result.error_msg) {
- api._logger.debug("._doCall() Error");
- aListener.onError(api.getError(result.error_code,
- result.error_msg));
- } else {
- api._logger.debug("._doCall() Success");
- aListener.onSuccess(result);
- }
- } else {
- // http errors
- api._logger.debug("._doCall() HTTP Error");
- aListener.onError(api.getHTTPError(req.status));
- }
- } catch (ex) {
- // XMLHTTPERROR (connection lost)
- api._logger.debug(".doCall() " + ex);
- aListener.onError(api.getHTTPError("9001"));
- }
- }
- }
- try {
- // just for debug output
- aUrl.match(/call_id=(.+)&/);
- var call_id = RegExp.$1;
- this._logger.debug("sending call_id: " + call_id);
- } catch (ex) {
- }
- this.req.send(null);
- };
-
-
- //*****************************************************
- //* xpcom constructor/info
- //*****************************************************
-
-
- facebookAPI.prototype.flags = CI.nsIClassInfo.SINGLETON;
- facebookAPI.prototype.classDescription = "Facebook JS API";
- facebookAPI.prototype.getInterfaces = function (count) {
- var interfaceList = [
- CI.flockIFacebookAPI,
- CI.nsIClassInfo
- ];
- count.value = interfaceList.length;
- return interfaceList;
- }
-
- facebookAPI.prototype.getHelperForLanguage = function (count) { return null; }
-
- facebookAPI.prototype.QueryInterface = function (iid) {
- if (!iid.equals(CI.flockIFacebookAPI) &&
- !iid.equals(CI.nsIClassInfo) &&
- !iid.equals(CI.nsISupports)) {
- throw CR.NS_ERROR_NO_INTERFACE;
- }
- return this;
- }
-
- facebookAPI.prototype.createPhoto = function () {
- return (new facebookPhoto().QueryInterface(CI.flockIPhoto));
- }
-
- var FB_API_Module = new Object();
-
- FB_API_Module.registerSelf = function (compMgr, fileSpec, location, type) {
- compMgr = compMgr.QueryInterface(CI.nsIComponentRegistrar);
-
- compMgr.registerFactoryLocation(FACEBOOK_API_CID,
- "Flock Facebook API JS Component",
- FACEBOOK_API_CONTRACTID,
- fileSpec,
- location,
- type);
- }
-
- FB_API_Module.getClassObject = function (compMgr, cid, iid) {
- if (!cid.equals(FACEBOOK_API_CID)) {
- throw CR.NS_ERROR_NO_INTERFACE;
- }
- if (!iid.equals(CI.nsIFactory)) {
- throw CR.NS_ERROR_NOT_IMPLEMENTED;
- }
- return FB_API_ServiceFactory;
- }
-
- FB_API_Module.canUnload = function (compMgr) {
- return true;
- }
-
- /* factory object */
- var FB_API_ServiceFactory = new Object();
-
- FB_API_ServiceFactory.createInstance = function (outer, iid) {
- if (outer != null) {
- throw CR.NS_ERROR_NO_AGGREGATION;
- }
- return (new facebookAPI()).QueryInterface(iid);
- }
-
- /* entrypoint */
- function NSGetModule(compMgr, fileSpec) {
- return FB_API_Module;
- }
-